package com.rgs.gateway.web.common.filter;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.rgs.gateway.authentication.feignclient.client.AuthFeignClient;
import com.rgs.gateway.common.enums.SystemErrorEnum;
import com.rgs.gateway.common.pojo.Result;
import com.rgs.gateway.web.common.config.value.IgnoreAuthUrlConfig;
import com.rgs.gateway.web.utils.IPAddressUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author RenGaoshuai
 * @date 2024/7/1 13:24
 * @description 鉴权拦截器
 */
@Configuration
@Slf4j
@Order(1)
public class AccessAuthFilter implements GlobalFilter {
    /**
     * 请求头变量名公钥
     */
    private static final String APP_KEY = "appKey";

    @Autowired
    private AuthFeignClient authFeignClient;
    @Autowired
    private IgnoreAuthUrlConfig ignoreAuthUrlConfig;


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String method = request.getMethodValue();
        String url = request.getPath().value();
        String ip = IPAddressUtils.getIpAddress(request);
        log.info("ip:{},url:{},method:{},headers:{}", ip, url, method, request.getHeaders());

        //这里可以添加忽略鉴权的url，直接放行到下一个filter
        if (!CollectionUtils.isEmpty(ignoreAuthUrlConfig.getIgnoreAuthUrls()) && ignoreAuthUrlConfig.getIgnoreAuthUrls().contains(url)) {
            return chain.filter(exchange);
        }

        //获取公钥
        String appKey = request.getHeaders().getFirst(APP_KEY);

        if (StrUtil.isBlank(appKey)) {
            log.error("请求头中缺少账户公钥信息");
            return unauthorized(exchange, Result.fail(SystemErrorEnum.UNAUTHORIZED));
        }

        //获取账户信息
        Result authUserResult = authFeignClient.authUserInfo(appKey);
        if (authUserResult.isFail() || Objects.isNull(authUserResult.getData())) {
            log.error("调用鉴权服务查询账户信息返回失败");
            return unauthorized(exchange, Result.fail(SystemErrorEnum.UNAUTHORIZED));
        }

        Map<String, Object> authMap = (Map<String, Object>) authUserResult.getData();

        //添加请求头
        JSONObject additionalInfo = JSONUtil.parseObj(MapUtil.getStr(authMap, "additionalInfo", "{}"));
        ServerHttpRequest.Builder builder = request.mutate();
        Boolean flag = fillHeader(additionalInfo, request, builder);

        if (!flag) {
            log.error("解析账户信息请求头字段失败");
            return unauthorized(exchange, Result.fail(SystemErrorEnum.UNAUTHORIZED));
        }

        //调用鉴权服务看该账户是否有访问权限
        Result<Boolean> authHavResult = authFeignClient.permission(appKey, url, method);
        if (authHavResult.isSuccess() && Objects.equals(authHavResult.getData(), Boolean.TRUE)) {
            //进入下一个过滤器
            return chain.filter(exchange.mutate().request(builder.build()).build());
        }

        return unauthorized(exchange, Result.fail(SystemErrorEnum.NOT_PERMISSION));
    }

    /**
     * 头部添加信息
     *
     * @param authMap
     * @param builder
     */
    private Boolean fillHeader(Map<String, Object> authMap, ServerHttpRequest request, ServerHttpRequest.Builder builder) {
        AtomicReference<Boolean> flag = new AtomicReference<>(true);
        if (MapUtil.isNotEmpty(authMap)) {
            authMap.forEach((key, value) -> {
                if (Objects.equals(key, "authHeaderInfoList")) {
                    //获取头部验证信息集合
                    JSONArray objects = JSONUtil.parseArray(JSONUtil.toJsonStr(value));
                    flag.set(checkAuthInfo(request, objects));
                }
                builder.header(key, String.valueOf(value));
            });
        }
        return flag.get();
    }

    /**
     * 头部信息验证
     *
     * @param request
     * @param objects
     * @return
     */
    private Boolean checkAuthInfo(ServerHttpRequest request, JSONArray objects) {
        AtomicReference<Boolean> flag = new AtomicReference<>(false);
        //头部信息进行校验
        objects.forEach(info -> {

            JSONObject jsonObject = JSONUtil.parseObj(info);
            jsonObject.forEach((key, value) -> {
                String headerValue = request.getHeaders().getFirst(key);
                if (StrUtil.isNotBlank(headerValue) && Objects.equals(headerValue, value)) {
                    flag.set(true);
                }
            });
        });
        return flag.get();
    }

    /**
     * 网关拒绝,返回信息
     *
     * @param
     */
    private Mono<Void> unauthorized(ServerWebExchange serverWebExchange, Result result) {
        serverWebExchange.getResponse().setStatusCode(HttpStatus.valueOf(result.getCode()));
        DataBuffer buffer = serverWebExchange.getResponse().bufferFactory().wrap(JSONUtil.toJsonStr(result).getBytes());
        return serverWebExchange.getResponse().writeWith(Flux.just(buffer));
    }
}
